Skip to content

Detect mask hierarchy cycles before reducing direct edges - #200

Merged
guijacquemet merged 3 commits into
mainfrom
claude/mask-cycle-reduction
Nov 7, 2026
Merged

guijacquemet merged 3 commits into
mainfrom
claude/mask-cycle-reduction

Conversation

@guijacquemet

Copy link
Copy Markdown
Contributor

Fixes #197

What changed and why

_relations marked an edge direct=False whenever a two-step path existed (transitive reduction), which assumes an acyclic graph. For a dense or chorded cycle every edge has such a path, so MaskReport.hierarchy came out empty, no mask_hierarchy_cycle warning fired, and the masks looked like separate roots.

Now strongly connected sets are computed on the full nested-relation graph first. Edges inside a set stay direct (so the cycle is reported by the existing detection and warning); the transitive reduction runs only between sets. For acyclic hierarchies every set is a single mask, so the direct edges are exactly as before. mask_scan.py is 599 lines (limit 600).

Regression tests (tests/test_mask_hierarchy_cycles.py, through _relations)

  • test_a_complete_three_cycle_keeps_its_edges_and_warns: fails on old code, passes now.
  • test_a_cycle_with_a_chord_keeps_its_edges_and_warns: fails on old code, passes now.
  • test_the_reduction_runs_between_cycles_and_acyclic_edges_are_unchanged: the acyclic chain part gives the same direct edges as before; the mixed cycle-below-a-parent part fails on old code. Passes now.
  • The existing acyclic test_nested_hierarchy_keeps_direct_parents_only still passes unchanged.

Checks

python tools/check.py: 3899 passed, 4 skipped (case-insensitive file system x3 files, file-mode permissions x1; skips not counted as passes), 2 warnings, code rules OK.


Generated by Claude Code

_relations removed every edge with a two-step path, which empties a dense or
chorded cycle and hid the mask_hierarchy_cycle warning. Strongly connected
sets are now found first: edges inside one stay direct, and the transitive
reduction runs only between sets. Acyclic hierarchies are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm
@guijacquemet
guijacquemet merged commit bcbb673 into main Nov 7, 2026
7 checks passed
@guijacquemet
guijacquemet deleted the claude/mask-cycle-reduction branch November 7, 2026 19:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Mask hierarchy: transitive reduction before cycle detection hides dense cycles

2 participants